package chloroform

import _root_.util.logging.Logged
import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
import argo.jdom.JsonRootNode
import util.{JsonRendererUtil, JsonUtils}
import argo.jdom.JsonNodeBuilders._
import java.lang.String
import javax.servlet._

/**
 * <ul>
 *   <li>Handles both GET and POST messages
 *   <li>Parses the message from the request
 *   <li>Determines which handler the message belongs to
 *   <li>Delegates dispatching to the selected handler
 * </ul>
 */
//class MessageDispatcherServlet extends HttpServlet with Logged {
class MessageDispatcherServlet extends Filter with Logged {

  var handlerChain: List[MessageHandlerFactory] = null

  def init(config: FilterConfig) = {
    val isMetaDisp: String = config.getInitParameter("isMetaDispatcher")
    if (isMetaDisp != null && isMetaDisp == "true")
      handlerChain = List(MetaStarterConfigMessageHandlerFactory, MetaProjectConfigSessionFactory)
    else
      handlerChain = List(StarterConfigMessageHandlerFactory, ProjectConfigSessionFactory)
  }

  def destroy = {}

  def doFilter(req: ServletRequest, resp: ServletResponse, chain: FilterChain) = {
    val httpReq = req.asInstanceOf[HttpServletRequest]
    val httpResp = resp.asInstanceOf[HttpServletResponse]
    httpReq.getMethod match {
      case "GET" => doGet(httpReq, httpResp, chain)
      case "POST" => doPost(httpReq, httpResp, chain)
      case _ => chain.doFilter(req, resp)
    }
  }

  def doGet(req: HttpServletRequest, resp: HttpServletResponse, chain: FilterChain) = {
    val parameterNames: java.util.Enumeration[String] = req.getParameterNames

    val message = anObjectBuilder
    // todo: some filtering parameters will be necessary
    while (parameterNames.hasMoreElements) {
      val parameter = parameterNames.nextElement
      val value = req.getParameter(parameter)
      message.withField(parameter, aStringBuilder(value))
    }

    dispatchMessage(message.build, req, resp, chain)
  }

  def doPost(req: HttpServletRequest, resp: HttpServletResponse, chain: FilterChain) = {
    val message: JsonRootNode = JsonUtils.parser.parse(req.getReader)
    dispatchMessage(message, req, resp, chain)
  }

  def renderError(t: scala.Throwable, resp: HttpServletResponse): Unit = {
    val errorRoot = anObjectBuilder
    errorRoot.withField("msgType", aStringBuilder("Error"))
    errorRoot.withField("message", aStringBuilder(t.toString + " : " + t.getMessage))

    val errorRenderer = JsonRendererUtil.makeRenderer(errorRoot)
    JsonUtils.renderResponse(resp, errorRenderer)
  }

  def dispatchMessage(message: JsonRootNode, req: HttpServletRequest, resp: HttpServletResponse, chain: FilterChain): Unit = {
    try {
      dispatch(message, req, resp, chain)
    }
    catch {
      case t: Throwable => {
        // return the error as a JSON message
        severe(t.getMessage, t)
        renderError(t, resp)
      }
    }
  }

  def dispatch(message: JsonRootNode, req: HttpServletRequest, resp: HttpServletResponse, chain: FilterChain): Unit = {
    val messageType = message.getStringValue("msgType")

    val dispatched: Option[MessageHandlerFactory] = handlerChain.find(handlerFactory => {
      handlerFactory.getHandledMessageIds.values.find(_.toString == messageType) match {
        case None => false
        case Some(messageType) => {
          val handler = handlerFactory.getHandler(messageType, message, req)

          //val outputMessageBuilder = handler.handleMessage(message)

          val messageOutcome: MessageOutcome = handler.handleMessage(message)
          handlerFactory.returnHandler(handler, req)

          messageOutcome match {
            case ResponseRenderer(renderer) => {
              renderer.render(req, resp)
            }
            case Forwarder(target) => {
              req.getRequestDispatcher(target).forward(req, resp)
            }
          }

//          handlerFactory.returnHandler(handler, req)
//
//          val renderer = JsonRendererUtil.makeRenderer(outputMessageBuilder)
//          JsonUtils.renderResponse(resp, renderer)

          true
        }

      }
    })

    dispatched match {
      //case None => throw new ServletException("No message handler for " + messageType)
      case None => chain.doFilter(req, resp)
      case Some(handlerFactory) => {
        debug("Dispatching factory: " + handlerFactory)
      }
    }
  }

}